package org.springframework.boot.autoconfigure.netty.server;

import io.netty.bootstrap.AbstractBootstrap;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.netty.NettyProperties;
import org.springframework.boot.autoconfigure.netty.channelInitializer.AbstractChannelInitializer;
import org.springframework.boot.autoconfigure.netty.enums.StartUpMode;

import java.util.concurrent.TimeUnit;

/**
 * Author: huoxingzhi
 * Date: 2020/12/10
 * Email: hxz_798561819@163.com
 */
public class NettySocketServer {

    private final static Logger logger = LoggerFactory.getLogger(NettySocketServer.class);

    private EventLoopGroup bossGroup;
    private EventLoopGroup workGroup;
    private ServerBootstrap server;
    private ChannelFuture future;
    private NettyProperties nettyProperties;
    private Bootstrap client;

    public NettySocketServer(NettyProperties nettyProperties) {
        this.nettyProperties = nettyProperties;
        if(this.nettyProperties.getStartUpMode()==StartUpMode.SERVER || this.nettyProperties.getStartUpMode()==StartUpMode.WEBSOCKET || this.nettyProperties.getStartUpMode()==StartUpMode.HTTP){
            bossGroup = new NioEventLoopGroup(this.nettyProperties.getBossGroupLoopThreads());
            workGroup = new NioEventLoopGroup(this.nettyProperties.getWorkGroupLoopThreads());
            server = new ServerBootstrap();
        }

        if(this.nettyProperties.getStartUpMode()==StartUpMode.CLINET){
            workGroup = new NioEventLoopGroup(this.nettyProperties.getWorkGroupLoopThreads());
            client = new Bootstrap();
        }

    }

    public void initAndStart(AbstractChannelInitializer channelInitializer) throws InterruptedException {

        if(logger.isInfoEnabled()){
            logger.debug("{},channelInitializer is : {}",this.getClass().getName(),channelInitializer.toString());
        }

        if(this.nettyProperties.getStartUpMode()==StartUpMode.SERVER || this.nettyProperties.getStartUpMode()==StartUpMode.WEBSOCKET || this.nettyProperties.getStartUpMode()==StartUpMode.HTTP){
            ServerBootstrap serverBootstrap = server.group(bossGroup, workGroup)
                    .channel(NioServerSocketChannel.class);
            serverBootstrap = (ServerBootstrap) buildOptions(serverBootstrap);
            serverBootstrap.childHandler(channelInitializer);

            this.future = server
                    .bind(nettyProperties.getServerPort());
            logger.info("netty server 启动完毕...");
        }

        if(this.nettyProperties.getStartUpMode()==StartUpMode.CLINET){
            Bootstrap bootstrap = client.group(workGroup)
                    .channel(NioServerSocketChannel.class);
            bootstrap = (Bootstrap) buildOptions(bootstrap);
            bootstrap.handler(channelInitializer);

            this.future = client
                    .connect(this.nettyProperties.getServerIpAddress(), this.nettyProperties.getServerPort());

            future.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture futureListener) throws Exception {
                    if (futureListener.isSuccess()) {
                        logger.info("Connect to server successfully!");
                    } else {
                        logger.info("Failed to connect to server, try connect after 10s");
                        futureListener.channel().eventLoop().schedule(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    initAndStart(channelInitializer);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }, 10, TimeUnit.SECONDS);
                    }
                }
            });
            logger.info("netty client 启动完毕...");
        }

    }

    private AbstractBootstrap buildOptions(AbstractBootstrap abstractBootstrap){

        AbstractBootstrap option = abstractBootstrap
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, nettyProperties.getChannelOptionConnectTimeoutMillis())
                .option(ChannelOption.SO_BACKLOG, nettyProperties.getChannelOptionSoBacklog())
                .option(ChannelOption.TCP_NODELAY, nettyProperties.isChannelOptionTcpNodelay())
                .option(ChannelOption.SO_KEEPALIVE, nettyProperties.getChannelOptionSoKeepalive())
                .option(ChannelOption.SO_LINGER, nettyProperties.getChannelOptionSoLinger())
                .option(ChannelOption.ALLOW_HALF_CLOSURE, nettyProperties.isChannelOptionAllowHalfClosure());
        return option;

    }
}
